﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WbsEditor
{
    public partial class IterationDashboard : ScrollableZoomableControl
    {
        #region Constructor and Initialization
        
        public IterationDashboard()
        {
            InitializeComponent();
            this.Center = false;
            this.Padding = new Padding(10);

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
        }

        private void InitDashboard()
        {
            if (null == Wbs)
                return;
            _data = new DashboardData();
            WbsTraverser.TraverseTree(Wbs.Root,
                delegate(WbsNode node)
                {
                    if (node.IsLeaf && !node.IsRoot)
                    {
                        WbsNodePair nodePair = new WbsNodePair(node);
                        _nodePairs.Add(nodePair);
                        _data.Add(nodePair.Clone);
                    }
                });
            this.Remeasure();
        }

        #endregion

        #region Properties and Fields

        private Point _mouseDownPoint;
        private bool _dragReady;
        private WbsNode _mouseDownSelectedNode;
        private bool _deselectAll;
        private List<WbsNodePair> _nodePairs = new List<WbsNodePair>();

        private WorkBreakdownStructure _wbs;
        private List<WbsNode> _selectedNodes = new List<WbsNode>();
        private DashboardData _data;

        public WbsTfsClient TfsClient
        {
            get;
            set;
        }

        public WorkBreakdownStructure Wbs
        {
            get
            {
                return _wbs;
            }
            set
            {
                _wbs = value;
                InitDashboard();
            }
        }

        public WbsSettings Settings
        {
            get;
            set;
        }

        public List<WbsNode> SelectedNodes
        {
            get
            {
                return _selectedNodes;
            }
        }

        public DashboardData Data
        {
            get
            {
                return _data;
            }
        }

        #endregion

        #region Change Tracking

        public bool HasChanges()
        {
            bool hasChanges = false;
            foreach (WbsNodePair pair in _nodePairs)
            {
                if (pair.Original.Iteration != pair.Clone.Iteration)
                    hasChanges = true;
            }
            return hasChanges;
        }

        public void CommitChanges()
        {
            if (!HasChanges())
                return;

            foreach (WbsNodePair pair in _nodePairs)
            {
                if (pair.Original.Iteration == pair.Clone.Iteration)
                    continue;

                try
                {
                    if (pair.Original.IsPublished && TfsClient.IsConnected)
                        TfsClient.RefreshWbsNode(Wbs, pair.Original);
                    pair.Original.Iteration = pair.Clone.Iteration;
                    if (pair.Original.IsPublished && TfsClient.IsConnected)
                        TfsClient.PublishAllFields(Wbs, pair.Original);
                }
                catch (Exception e)
                {
                    MessageBox.Show("While applying the new iteration to a node, the following error occurred:\n" + e.Message,
                        "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                }
            }
        }

        #endregion

        #region Overridden Event Handlers

        protected override Size Measure(Size available)
        {
            if (this._data == null)
                return new Size();

            Size result = WbsLayoutEngine.LayoutDashboard(_data, Settings, TfsClient);

            result.Height += 50;

            return result;
        }

        protected override void OnPaintContent(Graphics g)
        {
            if (null == Data)
                return;

            int i = 0;
            foreach (IterationNodes iteration in Data.IterationNodes)
            {
                int lineHeight = Math.Max(this.ContentSize.Height, this.VirtualSize.Height);
                g.DrawLine(Pens.Black, iteration.MaxX + 1, 0, iteration.MaxX + 1, lineHeight);
                string iterationPath = iteration.Iteration;
                string strippedIteration = iterationPath.Substring(iterationPath.LastIndexOf('\\') + 1);

                g.DrawString(strippedIteration, Settings.NormalFont, Brushes.Black,
                    new RectangleF(iteration.MinX, Settings.VerticalPadding, Settings.DefaultNodeSize.Width,
                        2 * Settings.VerticalPaddingAtRoot));

                foreach (WbsNode node in iteration.NodeList)
                {
                    WbsRenderer.RenderNode(node, Settings, g, Wbs, "");
                }
                ++i;
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (Data == null)
            {
                return;
            }
            base.OnMouseDown(e);

            Invalidate();

            this._mouseDownPoint = e.Location;
            this._mouseDownSelectedNode = null;

            WbsNode node = WbsLayoutEngine.FindNode(Data, this.ClientPointToContent(this._mouseDownPoint));
            if (null == node)
            {
                ClearSelectedNodes();
                return;
            }

            _dragReady = true;

            if (!node.IsSelected)
            {
                if (!Control.ModifierKeys.HasFlag(Keys.Control) && !Control.ModifierKeys.HasFlag(Keys.Shift))
                    ClearSelectedNodes();

                AddSelectedNode(node);
            }
            else if (this.SelectedNodes.Count == 1
                || Control.ModifierKeys.HasFlag(Keys.Control)
                || Control.ModifierKeys.HasFlag(Keys.Shift))
            {
                this._mouseDownSelectedNode = node;
            }
            else //Multiple nodes selected
            {
                this._deselectAll = true;
                this._mouseDownSelectedNode = node;
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (e.Button != MouseButtons.Left || !this._dragReady)
                return;

            WbsNode node = WbsLayoutEngine.FindNode(Data, this.ClientPointToContent(this._mouseDownPoint));
            if (Math.Abs(e.X - this._mouseDownPoint.X) > 5
                || Math.Abs(e.Y - this._mouseDownPoint.Y) > 5)
            {
                this._dragReady = false;
                DoDragDrop(node, DragDropEffects.Move);
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (this._mouseDownSelectedNode != null)
            {
                if (this._deselectAll)
                {
                    this.ClearSelectedNodes();
                    this.AddSelectedNode(this._mouseDownSelectedNode);
                }
                else
                {
                    this.RemoveSelectedNode(this._mouseDownSelectedNode);
                }
            }

            this._mouseDownSelectedNode = null;
            this._dragReady = false;
            this._deselectAll = false;

            base.OnMouseUp(e);
        }

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            base.OnDragOver(drgevent);

            drgevent.Effect = DragDropEffects.Move;
        }

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            base.OnDragDrop(drgevent);

            Point p = ClientPointToContent(PointToClient(new Point(drgevent.X, drgevent.Y)));
            string droppedIteration = FindDropIteration(p);
            if (null == droppedIteration)
                return;

            ApplyIterationToSelectedNodes(droppedIteration);
        }

        #endregion

        #region Iterations

        public void AddIterationToDisplay(string iterationName)
        {
            if (Data == null)
                return;

            Data.AddIteration(iterationName);
            Data.RefreshData();
            this.Remeasure();
        }

        private void ApplyIterationToSelectedNodes(string droppedIteration)
        {
            foreach (WbsNode node in SelectedNodes)
            {
                ApplyIterationToNode(droppedIteration, node);
            }

            Data.RefreshData();
            this.Remeasure();
        }

        private void ApplyIterationToNode(string iteration, WbsNode node)
        {
            node.Iteration = iteration;
        }

        private string FindDropIteration(Point p)
        {
            for (int i = Data.IterationNodes.Length - 1; i >= 0; --i)
            {
                if (p.X > Data.IterationNodes[i].MinX)
                    return Data.IterationNodes[i].Iteration;
            }
            return null;
        }

        #endregion

        #region Selection

        private void ClearSelectedNodes()
        {
            foreach (WbsNode node in SelectedNodes)
            {
                node.IsSelected = false;
            }
            SelectedNodes.Clear();

            this.Invalidate();
        }
        
        private void AddSelectedNode(WbsNode node)
        {
            node.IsSelected = true;
            SelectedNodes.Add(node);
            this.Invalidate();
        }

        private void RemoveSelectedNode(WbsNode node)
        {
            node.IsSelected = false;
            SelectedNodes.Remove(node);
            this.Invalidate();
        }

        #endregion
    }

    public class DashboardData
    {
        public DashboardData()
        {
        }

        private List<WbsNode> _nodeList = new List<WbsNode>();
        private bool _refreshNeeded = false;

        public void Add(WbsNode node)
        {
            _nodeList.Add(node);
            _refreshNeeded = true;
        }

        public void RefreshData()
        {
            _refreshNeeded = true;
        }

        private List<string> _seenIterations = new List<string>();
        private IterationNodes[] _iterationNodes;
        public IterationNodes[] IterationNodes
        {
            get
            {
                if (_refreshNeeded || null == _iterationNodes)
                {
                    Dictionary<string, IterationNodes> iterations = new Dictionary<string, WbsEditor.IterationNodes>();
                    foreach (string iterationName in _seenIterations)
                    {
                        iterations[iterationName] = new IterationNodes(iterationName);
                    }
                    foreach (WbsNode node in _nodeList)
                    {
                        string iteration = node.Iteration ?? String.Empty;
                        if (!iterations.ContainsKey(iteration))
                        {
                            iterations[iteration] = new IterationNodes(iteration);
                        }
                        iterations[iteration].NodeList.Add(node);
                    }

                    _iterationNodes = iterations.Values.ToArray();
                    Array.Sort(_iterationNodes);
                    foreach (IterationNodes iter in _iterationNodes)
                    {
                        AddIteration(iter.Iteration);
                    }

                    _refreshNeeded = false;
                }
                return _iterationNodes;
            }
        }

        public void AddIteration(string iterationName)
        {
            if (!_seenIterations.Contains(iterationName))
                _seenIterations.Add(iterationName);
            RefreshData();
        }
    }

    public class WbsNodePair
    {
        public WbsNodePair(WbsNode node)
        {
            Original = node;
            Clone = node.Clone(true);
        }

        public WbsNode Original
        {
            get;
            set;
        }

        public WbsNode Clone
        {
            get;
            set;
        }
    }

    public class IterationNodes : IComparable
    {
        public IterationNodes(string iteration)
        {
            NodeList = new List<WbsNode>();
            Iteration = iteration;
            MinX = 0;
            MaxX = 0;
        }

        public string Iteration
        {
            get;
            set;
        }

        public int MinX
        {
            get;
            set;
        }

        public int MaxX
        {
            get;
            set;
        }

        public List<WbsNode> NodeList
        {
            get;
            set;
        }

        public int CompareTo(object o)
        {
            IterationNodes other = o as IterationNodes;
            if (null == other)
                return -1;
            return Iteration.CompareTo(other.Iteration);
        }
    }
}
